using System;
using UnityEngine;

namespace UnityX
{
    public partial class RecorderVideo
    {
        public enum AudioInputType
        {
            Unknown,
            Global,
            AudioListener,
            AudioClip,
            AudioSource,
            Microphone,
        }
        internal class RecorderVideoAudioUpdate : MonoBehaviour
        {
            public Action<float[]> onAudioRead;
            public bool mute = false;
            private void OnAudioFilterRead(float[] data, int channels)
            {
                onAudioRead?.Invoke(data);
                if (mute)
                {
                    Array.Clear(data, 0, data.Length);
                }
            }
        }
        //
        private AudioListener audioListener = null;
        private AudioClip audioClip = null;

        private RecorderVideoAudioUpdate audioMonoBehaviour = null;
        private AudioSource audioSource = null;

        private AudioInputType audioInputType = AudioInputType.Unknown;

        /// <summary>
        /// 设置全局音频输入
        /// </summary>
        /// <param name="audioBitRate">音频比特率，单位为比特每秒</param>
        /// <exception cref="InvalidOperationException"></exception>
        public void SetAudioInput(int audioBitRate = 64_000)
        {
            AudioListener[] audioListeners = Resources.FindObjectsOfTypeAll<AudioListener>();
            if (audioListeners.Length == 0)
            {
                throw new InvalidOperationException(@"场景中没有AudioListener，设置声音输入失败！");
            }
            if (audioListeners.Length > 1)
            {
                throw new InvalidOperationException(@"场景中存在多个AudioListener，请手动指定！");
            }
            this.audioListener = audioListeners[0];
            this.audioClip = null;
            this.sampleRate = AudioSettings.outputSampleRate;
            this.channelCount = (int)AudioSettings.speakerMode;
            this.audioBitRate = audioBitRate;
            this.audioInputType = AudioInputType.Global;
        }
        public void SetAudioInput(AudioListener audioListener, int audioBitRate = 64_000)
        {
            this.audioListener = audioListener;
            this.audioClip = null;
            this.sampleRate = AudioSettings.outputSampleRate;
            this.channelCount = (int)AudioSettings.speakerMode;
            this.audioBitRate = audioBitRate;
            this.audioInputType = AudioInputType.AudioListener;
        }
        /// <summary>
        /// 设置麦克风音频输入
        /// </summary>
        /// <param name="audioClip">麦克风</param>
        /// <param name="audioBitRate">音频比特率，单位为比特每秒</param>
        public void SetAudioInput(AudioClip audioClip, int audioBitRate = 64_000)
        {
            this.audioListener = null;
            this.audioClip = audioClip;
            this.sampleRate = audioClip.frequency;
            this.channelCount = audioClip.channels;
            this.audioBitRate = audioBitRate;
            this.audioInputType = AudioInputType.AudioClip;
        }

        private unsafe void CommitSamples(float[] sampleBuffer)
        {
            duration = nowTime - beginTime;
            fixed (float* dataAddress = sampleBuffer)
            {
                RecorderVideoNativeAPI.CommitSamples(recorder, dataAddress, sampleBuffer.Length, duration);
            }
        }

        public void CreateAudioCommit()
        {
            switch (audioInputType)
            {
                case AudioInputType.Unknown:
                    break;
                case AudioInputType.Global:
                    {
                        audioMonoBehaviour = audioListener.gameObject.AddComponent<RecorderVideoAudioUpdate>();
                        audioMonoBehaviour.onAudioRead = CommitSamples;
                        audioMonoBehaviour.mute = false;
                    }
                    break;
                case AudioInputType.AudioListener:
                    {
                        audioMonoBehaviour = audioListener.gameObject.AddComponent<RecorderVideoAudioUpdate>();
                        audioMonoBehaviour.onAudioRead = CommitSamples;
                        audioMonoBehaviour.mute = false;
                    }
                    break;
                case AudioInputType.AudioClip:
                    {
                        audioSource = gameObject.AddComponent<AudioSource>();
                        audioSource.clip = audioClip;
                        audioSource.loop = true;
                        audioSource.Play();
                        audioMonoBehaviour = gameObject.AddComponent<RecorderVideoAudioUpdate>();
                        audioMonoBehaviour.onAudioRead = CommitSamples;
                        audioMonoBehaviour.mute = true;
                    }
                    break;
                case AudioInputType.AudioSource:
                    {
                        audioMonoBehaviour = audioSource.gameObject.AddComponent<RecorderVideoAudioUpdate>();
                        audioMonoBehaviour.onAudioRead = CommitSamples;
                        audioMonoBehaviour.mute = false;
                    }
                    break;
                case AudioInputType.Microphone:
                    {
                        audioSource = gameObject.AddComponent<AudioSource>();
                        audioSource.clip = audioClip;
                        audioSource.loop = true;
                        audioSource.Play();
                        audioMonoBehaviour = gameObject.AddComponent<RecorderVideoAudioUpdate>();
                        audioMonoBehaviour.onAudioRead = CommitSamples;
                        audioMonoBehaviour.mute = true;
                    }
                    break;
                default:
                    break;
            }
        }
        public void DestroyAudioCommit()
        {
            if (audioMonoBehaviour != null)
            {
                audioMonoBehaviour.onAudioRead = null;
                GameObject.Destroy(audioMonoBehaviour);
                audioMonoBehaviour = null;
            }
            audioListener = null;
            audioClip = null;
            audioSource = null;
        }
    }
}